#!/usr/bin/env bash
# 一键在容器内完成“可信启动 ISO”签名与重打包的脚本
#
# 目的：
#   - 在指定容器中，自动完成 shim(bootaa64.efi) 的 RSA 签名；
#   - 尝试对 grub、内核进行 SM2+SM3 签名（如环境不支持则降级为告警不中断）；
#   - 将签名结果写回 efi.img，并基于解包树 /workiso 重打包生成新的 ISO；
#   - 将 secure.iso 以及证书 DER 与关键产物回拷到宿主机。
#
# 适用：
#   - 容器名称默认 openeuler_iso_secuce，容器内已安装 pesign / NSS / openssl，或可联网安装 xorriso / mtools。
#
# 顶层执行步骤（供快速理解）：
#   1) 读取参数，检查 docker 与容器状态；
#   2) 可选清理容器 /root；拷贝宿主 ISO → 容器 /root/input.iso；
#   3) 进入容器：
#      3.1) 准备工具；将 /root/input.iso 解包到 /workiso；
#      3.2) 确认/提取 bootaa64.efi；
#      3.3) 生成或加载 RSA 证书与 NSS，导入 PKCS#12，选择“确为 RSA”的证书昵称；
#      3.4) 使用 RSA 对 shim(bootaa64.efi) 签名；
#      3.5) 准备 SM2 证书，尝试对 grub 与内核进行 SM2 签名（失败打印 WARN 并继续）；
#      3.6) 回写 bootaa64.efi/grubaa64.efi 至 efi.img；
#      3.7) 用 xorriso 基于 /workiso 重打包生成 ISO；导出 RSA/SM2 DER；
#   4) 返回宿主：复制 secure.iso 及 artifacts/* 到当前目录。
#
# 重要变量（可通过环境变量覆盖）：
#   - CONTAINER：容器名称（默认 openeuler_iso_secuce）
#   - HOST_ISO：宿主机上的源 ISO 文件路径（默认当前目录下 openeuler-image-*.iso）
#   - INPUT_EFI：容器内待签名的 shim 路径（默认 /workiso/EFI/BOOT/bootaa64.efi）
#   - OUTPUT_EFI：容器内 shim 签名输出（默认 /root/bootaa64.efi.signed）
#   - NSS_DIR / PIN_FILE / P12_PW / NSS_PIN：NSS 数据库目录、PIN 文件、PKCS#12 密码、NSS PIN
#   - RSA_KEY / RSA_CRT：RSA 私钥与证书路径（shim 使用 RSA）
#   - SM2_KEY / SM2_CRT / SM2_DER：SM2 私钥/证书/DER（grub、内核使用 SM2）
#   - GRUB_EFI / KERNEL_IMG / EFI_IMG_PATH：grub、内核、efi.img 路径（均在容器内）
#   - SIGN_SHIM / SIGN_GRUB / SIGN_KERNEL / REPACK_ISO：是否签名与是否重打包 ISO 的开关
#   - ISO_VOLID / ISO_OUTPUT：重打包的卷标与输出路径
#   - TRACE：容器内 set -x 调试开关（1 开启）
#
# 使用示例：
#   # 可选：执行前清空容器 /root（危险操作，仅在临时环境使用）
#   # docker exec -it openeuler_iso_secuce bash -lc "rm -rf /root/*"
#   bash sign_efi_docker.sh
#   TRACE=1 SIGN_KERNEL=false ISO_VOLID=SecureISO-20250926 bash sign_efi_docker.sh
#   CONTAINER=myctr INPUT_EFI=/path/in/ctr.efi OUTPUT_EFI=/root/out.efi.signed bash sign_efi_docker.sh

set -euo pipefail

# =========================
# 步骤 A：读取配置与默认值
#   A-0：指定需要签名的原始 ISO（宿主机路径）
# =========================
HOST_ISO=${HOST_ISO:-openeuler-image-kp920-20250628093650.iso}

CONTAINER=${CONTAINER:-openeuler_iso_secuce}   # 容器名称
INPUT_EFI=${INPUT_EFI:-/workiso/EFI/BOOT/bootaa64.efi}  # 容器内待签名的EFI文件（优先从解包的ISO树）
OUTPUT_EFI=${OUTPUT_EFI:-/root/bootaa64.efi.signed} # 容器内输出的签名EFI文件
NSS_DIR=${NSS_DIR:-/root/certdb2}   # NSS证书数据库目录
PIN_FILE=${PIN_FILE:-/root/db.pw}   # NSS数据库PIN文件路径
P12_PW=${P12_PW:-1234}              # 生成PKCS#12文件时使用的口令
NSS_PIN=${NSS_PIN:-abcd}            # NSS数据库PIN
RSA_KEY=${RSA_KEY:-/root/rsa.key}   # RSA私钥路径
RSA_CRT=${RSA_CRT:-/root/rsa.crt}   # RSA证书路径
CERT_NICKNAME=${CERT_NICKNAME:-}    # 证书昵称（可留空，自动解析）
DO_CLEAN_ROOT=${DO_CLEAN_ROOT:-false} # 是否在执行前清空容器/root目录（true/false）
TRACE=${TRACE:-0}                   # 是否打印容器内详细命令（1开启/0关闭）

# 构建 shim 相关配置（可选）
SHIM_BUILD=${SHIM_BUILD:-false}     # 是否在容器内编译生成 shim 的 EFI（默认为 false）
SHIM_BRANCH=${SHIM_BRANCH:-openEuler-22.03-LTS-SP1}  # shim 源码分支
SHIM_SRC=${SHIM_SRC:-/root/shim}    # shim 源码目录
USE_BUILT_SHIM=${USE_BUILT_SHIM:-true}  # 若成功构建，是否改为签名刚构建的 shim EFI

# SM2 证书（供 shim 验证 grub）及依赖
SM2_KEY=${SM2_KEY:-/root/sm2.key}
SM2_CRT=${SM2_CRT:-/root/sm2.crt}
SM2_DER=${SM2_DER:-/root/sm2.der}
P12_SM2=${P12_SM2:-/root/sm2.p12}
INSTALL_DEPS=${INSTALL_DEPS:-true}  # 构建 shim 前是否自动安装依赖（需要网络）

# 额外签名目标与打包控制（可选）
SIGN_SHIM=${SIGN_SHIM:-true}        # 是否签名 shim（bootaa64.efi，RSA）
SIGN_GRUB=${SIGN_GRUB:-true}        # 是否签名 grub（grubaa64.efi，SM2+SM3）
SIGN_KERNEL=${SIGN_KERNEL:-false}   # 是否签名内核（zImage 或 vmlinuz，SM2+SM3）
GRUB_EFI=${GRUB_EFI:-/workiso/EFI/BOOT/grubaa64.efi}  # grub EFI 路径（容器内）
KERNEL_IMG=${KERNEL_IMG:-/workiso/zImage}             # 内核镜像路径（容器内）
SM2_CERT_NICKNAME=${SM2_CERT_NICKNAME:-}          # SM2 证书昵称（可留空自动解析）
SM2_DIGEST=${SM2_DIGEST:-sm3}                     # SM2 摘要算法，默认 sm3
MTOOLS_UPDATE=${MTOOLS_UPDATE:-true}              # 是否用 mtools 更新 efi.img
EFI_IMG_PATH=${EFI_IMG_PATH:-/workiso/efi.img}    # efi.img 路径（容器内）
REPACK_ISO=${REPACK_ISO:-true}                    # 是否在容器内用 xorriso 重新打包 ISO
ISO_VOLID=${ISO_VOLID:-SecureISO}                 # ISO 卷标
ISO_OUTPUT=${ISO_OUTPUT:-/root/secure.iso}        # 输出 ISO 路径（容器内）

need_cmd() { command -v "$1" >/dev/null 2>&1 || { echo "[ERR] Missing command: $1" >&2; exit 1; }; }
# =========================
# 步骤 B：检查宿主环境依赖
#   - docker 命令可用
#   - 目标容器正在运行
# =========================
need_cmd docker  # 检查宿主机上是否安装 docker 命令

# Check container running
if ! docker inspect -f '{{.State.Running}}' "$CONTAINER" >/dev/null 2>&1; then
  # 步骤 C：检查目标容器是否处于运行状态
  echo "[ERR] Container not found or not running: $CONTAINER" >&2
  exit 1
fi

# 检查输入文件（签名前不强制要求存在；后续会尝试从 ISO/efi.img 准备）
if ! docker exec -i "$CONTAINER" bash -lc "test -r '$INPUT_EFI'"; then
  echo "[WARN] Input EFI not found yet: $INPUT_EFI (will attempt to extract from ISO/efi.img inside container)"
fi

# 打印目标容器与主要路径（便于快速核对）
echo "[INFO] Step 0: Target container and files"
echo "[INFO]   CONTAINER=$CONTAINER"
echo "[INFO]   INPUT_EFI=$INPUT_EFI"
echo "[INFO]   OUTPUT_EFI=$OUTPUT_EFI"

# =========================
# 步骤 D（可选）：清空容器 /root，确保干净环境
#   注意：危险操作，仅建议在临时容器中使用
# =========================
if [ "$DO_CLEAN_ROOT" = "true" ]; then
  # 步骤 E（可选）：在容器内清空 /root 目录，确保环境干净（危险操作，请谨慎使用）
  echo "[WARN] DO_CLEAN_ROOT=true -> Removing ALL files under /root in container..."
  docker exec -i "$CONTAINER" bash -lc 'rm -rf /root/*'
fi

# =========================
# 步骤 E：拷贝宿主 ISO → 容器 /root/input.iso（若存在）
# =========================
if [ -f "$HOST_ISO" ]; then
  echo "[INFO] Copy host ISO into container: $HOST_ISO -> /root/input.iso"
  docker cp "$HOST_ISO" "$CONTAINER":/root/input.iso
else
  echo "[INFO] HOST_ISO not found on host or not set: $HOST_ISO (skip copy)"
fi

docker exec -i \
  -e INPUT_EFI="$INPUT_EFI" \
  -e OUTPUT_EFI="$OUTPUT_EFI" \
  -e NSS_DIR="$NSS_DIR" \
  -e PIN_FILE="$PIN_FILE" \
  -e P12_PW="$P12_PW" \
  -e NSS_PIN="$NSS_PIN" \
  -e RSA_KEY="$RSA_KEY" \
  -e RSA_CRT="$RSA_CRT" \
  -e SHIM_BUILD="$SHIM_BUILD" \
  -e SHIM_BRANCH="$SHIM_BRANCH" \
  -e SHIM_SRC="$SHIM_SRC" \
  -e USE_BUILT_SHIM="$USE_BUILT_SHIM" \
  -e SM2_KEY="$SM2_KEY" \
  -e SM2_CRT="$SM2_CRT" \
  -e SM2_DER="$SM2_DER" \
  -e P12_SM2="$P12_SM2" \
  -e INSTALL_DEPS="$INSTALL_DEPS" \
  -e TRACE="$TRACE" \
  -e CERT_NICKNAME="$CERT_NICKNAME" \
  -e RSA_P12_NAME="${RSA_P12_NAME:-RSA-SB}" \
  -e SM2_P12_NAME="${SM2_P12_NAME:-SM2-SB}" \
  -e SIGN_SHIM="$SIGN_SHIM" \
  -e SIGN_GRUB="$SIGN_GRUB" \
  -e SIGN_KERNEL="$SIGN_KERNEL" \
  -e GRUB_EFI="$GRUB_EFI" \
  -e KERNEL_IMG="$KERNEL_IMG" \
  -e SM2_CERT_NICKNAME="$SM2_CERT_NICKNAME" \
  -e SM2_DIGEST="$SM2_DIGEST" \
  -e MTOOLS_UPDATE="$MTOOLS_UPDATE" \
  -e EFI_IMG_PATH="$EFI_IMG_PATH" \
  -e REPACK_ISO="$REPACK_ISO" \
  -e ISO_VOLID="$ISO_VOLID" \
  -e ISO_OUTPUT="$ISO_OUTPUT" \
  "$CONTAINER" bash -s <<'INCONTAINER'
set -euo pipefail

log() { echo "[IN-CTR] $*"; }
need_cmd() { command -v "$1" >/dev/null 2>&1 || { echo "[ERR] Missing command: $1" >&2; exit 2; }; }
  # 步骤 1：确认容器内所需工具存在（pesign/NSS/openssl）
  for c in pesign certutil pk12util openssl; do need_cmd "$c"; done

# 可选：打印容器内部命令（用于调试）
if [ "${TRACE:-0}" = "1" ]; then
  set -x
fi

# =========================
# 步骤 0（容器内）：将 /root/input.iso 解包到 /workiso（若存在）
# =========================
log "Step 0: Prepare /workiso from /root/input.iso if present"
if [ -f /root/input.iso ]; then
  if ! command -v xorriso >/dev/null 2>&1; then
    (yum -y install xorriso || dnf -y install xorriso) || true
  fi
  mkdir -p /workiso
  # 若目录为空或未找到关键文件，则尝试解包
  if [ ! -f /workiso/efi.img ] && [ -s /root/input.iso ] && command -v xorriso >/dev/null 2>&1; then
    rm -rf /workiso/*
    xorriso -osirrox on -indev /root/input.iso -extract / /workiso || echo "[WARN] xorriso extract failed, proceeding with existing tree if any"
  fi
fi

# =========================
# 步骤 1（容器内）：确认/提取待签名 shim（bootaa64.efi）
#   - 优先读取 /workiso/EFI/BOOT/bootaa64.efi
#   - 若缺失，则尝试从 efi.img 中提取到 /root/efi_tmp
# =========================
log "Step 1: Verify or extract input EFI file"
FROM_EFI_IMG=0
if [ ! -r "$INPUT_EFI" ]; then
  if [ -f "${EFI_IMG_PATH:-/workiso/efi.img}" ]; then
    if ! command -v mcopy >/dev/null 2>&1; then
      (yum -y install mtools || dnf -y install mtools) || true
    fi
    if command -v mcopy >/dev/null 2>&1; then
      mkdir -p /root/efi_tmp
      if mcopy -n -i "$EFI_IMG_PATH" ::/EFI/BOOT/bootaa64.efi /root/efi_tmp/bootaa64.efi 2>/dev/null; then
        INPUT_EFI=/root/efi_tmp/bootaa64.efi
        FROM_EFI_IMG=1
        log "Extracted bootaa64.efi from efi.img to $INPUT_EFI"
      fi
    fi
  fi
fi
if [ ! -r "$INPUT_EFI" ] && [ "${SIGN_SHIM:-true}" = "true" ]; then
  echo "[ERR] Input EFI not readable: $INPUT_EFI" >&2
  exit 3
fi
# 步骤 3：创建输出目录（容器内）
mkdir -p "$(dirname "$OUTPUT_EFI")"

# =========================
# 步骤 2（容器内）：准备 RSA 密钥与证书（shim 使用 RSA）
#   - 如缺失则自动生成 4096 位 RSA 与自签证书
# =========================
# Step 2: Prepare RSA key/cert if missing
if [ ! -s "$RSA_KEY" ] || [ ! -s "$RSA_CRT" ]; then
  log "Generating RSA key and self-signed certificate..."
  openssl genrsa -out "$RSA_KEY" 4096
  openssl req -new -key "$RSA_KEY" -out /root/rsa.csr -subj '/C=AA/ST=BB/O=CC/OU=DD/CN=secure boot BIOS'
  openssl x509 -req -days 365 -in /root/rsa.csr -signkey "$RSA_KEY" -out "$RSA_CRT"
fi

# 可选：构建 shim 并产出 EFI（AArch64）。需要容器为 aarch64 或具备交叉工具链。
# =========================
if [ "${SHIM_BUILD:-false}" = "true" ]; then
  log "Step 2.1: Build shim EFI (optional)"
  ARCH=
  ARCH=$(uname -m || true)
  if echo "$ARCH" | grep -qi 'aarch64'; then
    # 可尝试直接本机构建
    if [ "${INSTALL_DEPS:-true}" = "true" ]; then
      log "Installing build dependencies (requires network, best-effort)..."
      # 开放两个包管理器的兜底，失败不退出
      (yum -y install elfutils-libelf-devel gcc gnu-efi gnu-efi-devel openssl-devel make git rpm-build efivar-devel rpm2cpio cpio || true)
      (dnf -y install elfutils-libelf-devel gcc gnu-efi gnu-efi-devel openssl-devel make git rpm-build efivar-devel rpm2cpio cpio || true)
    fi

    # 准备 SM2 证书（供 shim 内置 vendor_cert 使用）
    if [ ! -s "$SM2_DER" ]; then
      log "Generating SM2 cert for shim vendor_cert..."
      openssl ecparam -genkey -name SM2 -out "$SM2_KEY"
      openssl req -new -sm3 -key "$SM2_KEY" -out /root/sm2.csr -subj '/C=AA/ST=BB/O=CC/OU=DD/CN=secure boot shim'
      openssl x509 -req -days 3650 -signkey "$SM2_KEY" -in /root/sm2.csr -out "$SM2_CRT"
      openssl x509 -in "$SM2_CRT" -out "$SM2_DER" -outform der
    fi

    # 获取 shim 源码
    if [ ! -d "$SHIM_SRC" ]; then
      log "Cloning shim source..."
      git clone https://gitee.com/src-openeuler/shim.git -b "$SHIM_BRANCH" --depth 1 "$SHIM_SRC"
    fi
    cd "$SHIM_SRC"

    # 修改 spec：开启商密并指定 vendor_cert
    if [ -f shim.spec ]; then
      log "Patching shim.spec to enable SM and set vendor_cert=$SM2_DER"
      # 开启商密
      if grep -q '^%global\s\+enable_sm' shim.spec; then
        sed -i "s/^%global\s\+enable_sm.*/%global enable_sm 1/" shim.spec
      else
        sed -i '1i %global enable_sm 1' shim.spec
      fi
      # 设置 vendor_cert
      if grep -q '^%global\s\+vendor_cert' shim.spec; then
        sed -i "s#^%global\s\+vendor_cert.*#%global vendor_cert $SM2_DER#" shim.spec
      else
        sed -i "1i %global vendor_cert $SM2_DER" shim.spec
      fi

      # 构建 RPM
      log "Building shim RPM via rpmbuild..."
      rpmbuild -ba shim.spec --define "_sourcedir $PWD" || {
        echo "[WARN] rpmbuild failed, will try to continue if artifacts exist." >&2
      }

      # 尝试查找产物 RPM 并解包提取 *.efi
      EFI_OUT_DIR=/root/shim_build
      mkdir -p "$EFI_OUT_DIR"
      RPM_PATH=
      RPM_PATH=$(ls -1 ~/rpmbuild/RPMS/*/shim-*.rpm 2>/dev/null | head -n1 || true)
      if [ -n "$RPM_PATH" ]; then
        log "Extracting EFI from $RPM_PATH"
        (cd "$EFI_OUT_DIR" && rpm2cpio "$RPM_PATH" | cpio -idmv) || true
        # 选择合适的 aa64 EFI
        CANDIDATE=$(find "$EFI_OUT_DIR" -type f -iname '*aa64*.efi' | head -n1 || true)
        if [ -n "$CANDIDATE" ]; then
          cp -f "$CANDIDATE" /root/bootaa64.shim.efi
          if [ "${USE_BUILT_SHIM:-true}" = "true" ]; then
            INPUT_EFI=/root/bootaa64.shim.efi
            log "Using built shim EFI as input: $INPUT_EFI"
          fi
        else
          echo "[WARN] Could not find aa64 EFI inside extracted RPM." >&2
        fi
      else
        echo "[WARN] Could not locate built shim RPM under ~/rpmbuild/RPMS." >&2
      fi
    else
      echo "[WARN] shim.spec not found under $SHIM_SRC; skip building shim." >&2
    fi
  else
    echo "[WARN] Container arch ($ARCH) is not aarch64; cross-build not implemented in this script. Skipping shim build." >&2
  fi
fi

# Fallback：若要求构建 shim 但未成功，或直接希望复用系统已有 shim，则尝试在常见路径中查找
if [ "${SHIM_BUILD:-false}" = "true" ] && [ ! -f /root/bootaa64.shim.efi ]; then
  log "Step 2.1-fallback: Try to locate existing shimaa64.efi in system"
  for p in \
    /boot/efi/EFI/openEuler/shimaa64.efi \
    /usr/share/efi/aarch64/shimaa64.efi \
    /usr/lib64/efi/shimaa64.efi \
    /usr/lib/efi/shimaa64.efi \
    /usr/lib64/shim/*aa64*.efi \
    /usr/lib/shim/*aa64*.efi; do
    if [ -f "$p" ]; then
      cp -f "$p" /root/bootaa64.shim.efi
      if [ "${USE_BUILT_SHIM:-true}" = "true" ]; then
        INPUT_EFI=/root/bootaa64.shim.efi
        log "Using located shim EFI as input: $INPUT_EFI"
      fi
      break
    fi
  done
fi

# =========================
# 步骤 3（容器内）：生成 PKCS#12（包含 RSA 证书/私钥），用于非交互导入 NSS
# =========================
# Step 3: Create PKCS#12 with known password
P12_FILE=/root/rsa_auto.p12
rm -f "$P12_FILE"
openssl pkcs12 -export -name "${RSA_P12_NAME:-RSA-SB}" -out "$P12_FILE" -inkey "$RSA_KEY" -in "$RSA_CRT" -passout pass:"$P12_PW"

# =========================
# 步骤 4（容器内）：创建 NSS 数据库 + 固定 PIN（便于非交互）
# =========================
# Step 4: Create NSS DB with fixed PIN
mkdir -p "$NSS_DIR"
echo -n "$NSS_PIN" > "$PIN_FILE"
if [ ! -f "$NSS_DIR/cert9.db" ]; then
  certutil -N -d "$NSS_DIR" -f "$PIN_FILE"
fi

# =========================
# 步骤 5（容器内）：导入 PKCS#12，并选择“确为 RSA”的证书昵称
#   - 优先匹配我们设置的友好名（RSA-SB）；
#   - 否则从 NSS 列表中挑选“公钥算法为 RSA”的证书；
#   - 最后兜底解析 pk12util 输出或列表最后一项。
# =========================
# Step 5: Import PKCS#12 and capture nickname from output (if not provided)
IMPORT_LOG=$(pk12util -d "$NSS_DIR" -i "$P12_FILE" -W "$P12_PW" -K "$NSS_PIN" 2>&1 || true)
echo "$IMPORT_LOG"

trim() { sed -e 's/^\s\+//' -e 's/\s\+$//'; }

if [ -z "${CERT_NICKNAME:-}" ]; then
  # 汇总 NSS 中的证书昵称列表（去掉信任列，仅第一列昵称），逐行遍历
  NICK_LIST=$(certutil -L -d "$NSS_DIR" | awk 'BEGIN{skip=1} /^Certificate Nickname/{next} /SSL,S\/MIME,JAR\/XPI/{next} /^-/{next} NF{print $0}' | sed -E 's/[[:space:]]{2,}.*$//')
  # 优先：如果存在我们指定的友好名
  if certutil -L -d "$NSS_DIR" -n "${RSA_P12_NAME:-RSA-SB}" >/dev/null 2>&1; then
    CERT_NICKNAME="${RSA_P12_NAME:-RSA-SB}"
  else
    # 其次：选择公钥算法为 RSA 的证书
    FOUND_RSA_NICK=
    while IFS= read -r nk; do
      [ -z "$nk" ] && continue
      if certutil -L -d "$NSS_DIR" -n "$nk" -a 2>/dev/null | openssl x509 -text -noout 2>/dev/null | grep -qi 'Public Key Algorithm: *RSA'; then
        FOUND_RSA_NICK="$nk"; break
      fi
    done <<EOF
$NICK_LIST
EOF
    if [ -n "$FOUND_RSA_NICK" ]; then
      CERT_NICKNAME="$FOUND_RSA_NICK"
    else
      # 兜底：解析 pk12util 输出或列表最后一项
      CERT_NICKNAME=$(echo "$IMPORT_LOG" | awk -F': ' '/using nickname:/ {print $NF; exit}') || true
      CERT_NICKNAME=$(echo "${CERT_NICKNAME:-}" | trim)
      if [ -z "$CERT_NICKNAME" ]; then
        CERT_NICKNAME=$(echo "$NICK_LIST" | tail -n1)
      fi
    fi
  fi
fi

# 校验昵称确实存在于 NSS DB，否则报错并给出列表
if ! certutil -L -d "$NSS_DIR" -n "$CERT_NICKNAME" >/dev/null 2>&1; then
  # 步骤 8：验证解析出的证书昵称是否在NSS数据库中存在
  echo "[ERR] Certificate nickname not found in NSS DB: '$CERT_NICKNAME'" >&2
  echo "[INFO] Current certificates in $NSS_DIR:" >&2
  certutil -L -d "$NSS_DIR" || true
  echo "[HINT] You can set CERT_NICKNAME explicitly to override detection." >&2
  exit 4
fi

# 进一步加强：按主题精确比对，强制选择与本地 RSA 证书主题一致的 NSS 证书，避免误用 SM2
RSA_SUBJ=$(openssl x509 -in "$RSA_CRT" -noout -subject 2>/dev/null | sed 's/^subject= *//')
if [ -n "$RSA_SUBJ" ]; then
  ALL_NICKS=$(certutil -L -d "$NSS_DIR" | awk 'BEGIN{skip=1} /^Certificate Nickname/{next} /SSL,S\/MIME,JAR\/XPI/{next} /^-/{next} NF{print $0}' | sed -E 's/[[:space:]]{2,}.*$//')
  FOUND_RSA_NICK=
  while IFS= read -r nk; do
    [ -z "$nk" ] && continue
    SUBJ=$(certutil -L -d "$NSS_DIR" -n "$nk" -a 2>/dev/null | openssl x509 -noout -subject 2>/dev/null | sed 's/^subject= *//')
    if [ -n "$SUBJ" ] && [ "$SUBJ" = "$RSA_SUBJ" ]; then
      FOUND_RSA_NICK="$nk"; break
    fi
  done <<EOF
$ALL_NICKS
EOF
  if [ -n "$FOUND_RSA_NICK" ]; then
    CERT_NICKNAME="$FOUND_RSA_NICK"
  fi
fi

log "Using certificate nickname: $CERT_NICKNAME"

# =========================
# 步骤 6（容器内）：签名 shim（bootaa64.efi，使用 RSA）
#   - 使用 --force 允许覆盖，便于幂等执行
# =========================
# Step 6: Sign the EFI binary（shim，RSA）
if [ "${SIGN_SHIM:-true}" = "true" ] && [ -r "$INPUT_EFI" ]; then
  pesign -n "$NSS_DIR" -c "$CERT_NICKNAME" -s -i "$INPUT_EFI" -o "$OUTPUT_EFI" --pinfile "$PIN_FILE" --force
  log "Signed shim: $INPUT_EFI -> $OUTPUT_EFI"
else
  log "Skip signing shim (SIGN_SHIM=$SIGN_SHIM)"
fi

# Step 7: Show signature (best-effort)         # 步骤 7（容器内）：展示签名信息（校验签名条目）
log "Signature info:"
pesign -S -i "$OUTPUT_EFI" || true

log "Signed file created at: $OUTPUT_EFI"      # 步骤 8（容器内）：输出最终签名文件路径

# =========================
# 步骤 9（容器内，可选）：准备 SM2 证书并签名 grub（SM2+SM3）
#   - 若环境不支持国密，打印 WARN 并继续，不中断主流程
# =========================
if [ "${SIGN_GRUB:-false}" = "true" ]; then
  log "Step 12: Prepare SM2 key/cert for grub signing"
  # 若缺少 SM2 证书，生成一套
  if [ ! -s "$SM2_KEY" ] || [ ! -s "$SM2_CRT" ]; then
    openssl ecparam -genkey -name SM2 -out "$SM2_KEY"
    openssl req -new -sm3 -key "$SM2_KEY" -out /root/sm2.csr -subj '/C=AA/ST=BB/O=CC/OU=DD/CN=secure boot shim'
    openssl x509 -req -days 3650 -signkey "$SM2_KEY" -in /root/sm2.csr -out "$SM2_CRT"
  fi
  # 导入 SM2 到 NSS（若未导入）
  if ! certutil -L -d "$NSS_DIR" | grep -qE "(${SM2_P12_NAME:-SM2-SB}|secure boot shim|SM2|sm2)"; then
    rm -f "$P12_SM2"; openssl pkcs12 -export -name "${SM2_P12_NAME:-SM2-SB}" -out "$P12_SM2" -inkey "$SM2_KEY" -in "$SM2_CRT" -passout pass:"$P12_PW"
    pk12util -d "$NSS_DIR" -i "$P12_SM2" -W "$P12_PW" -K "$NSS_PIN" || true
  fi
  # 解析 SM2 昵称
  if [ -z "${SM2_CERT_NICKNAME:-}" ]; then
    if certutil -L -d "$NSS_DIR" -n "${SM2_P12_NAME:-SM2-SB}" >/dev/null 2>&1; then
      SM2_CERT_NICKNAME="${SM2_P12_NAME:-SM2-SB}"
    else
      SM2_CERT_NICKNAME=$(certutil -L -d "$NSS_DIR" | awk 'BEGIN{skip=1} /^Certificate Nickname/{next} /SSL,S\/MIME,JAR\/XPI/{next} /^-/{next} NF{line=$0} END{print line}' | sed -E 's/[[:space:]]{2,}.*$//' )
      SM2_CERT_NICKNAME=${SM2_CERT_NICKNAME:-secure boot shim - CC}
    fi
  fi
  if ! certutil -L -d "$NSS_DIR" -n "$SM2_CERT_NICKNAME" >/dev/null 2>&1; then
    echo "[ERR] SM2 certificate nickname not found in NSS DB: '$SM2_CERT_NICKNAME'" >&2
    certutil -L -d "$NSS_DIR" || true
    exit 5
  fi
  # 若 grub 不在工作树中，则尝试从 efi.img 提取
  FROM_EFI_IMG_GRUB=0
  if [ ! -r "$GRUB_EFI" ] && [ -f "${EFI_IMG_PATH:-/workiso/efi.img}" ]; then
    if command -v mcopy >/dev/null 2>&1; then
      mkdir -p /root/efi_tmp
      if mcopy -n -i "$EFI_IMG_PATH" ::/EFI/BOOT/grubaa64.efi /root/efi_tmp/grubaa64.efi 2>/dev/null; then
        GRUB_EFI=/root/efi_tmp/grubaa64.efi
        FROM_EFI_IMG_GRUB=1
        log "Extracted grubaa64.efi from efi.img to $GRUB_EFI"
      fi
    fi
  fi
  if [ ! -r "$GRUB_EFI" ]; then
    echo "[WARN] GRUB EFI not found: $GRUB_EFI, skip grub signing" >&2
  else
    log "Step 13: Sign grub with SM2 ($SM2_DIGEST)"
    if ! pesign -n "$NSS_DIR" -c "$SM2_CERT_NICKNAME" -s -d "$SM2_DIGEST" -i "$GRUB_EFI" -o "$GRUB_EFI" --pinfile "$PIN_FILE" --force; then
      echo "[WARN] Grub signing failed (possibly unsupported digest/alg). Skipping grub signing."
    else
      pesign -S -i "$GRUB_EFI" || true
      log "Grub signed in-place: $GRUB_EFI"
    fi
    # 若来源于 efi.img，则回写
    if [ $FROM_EFI_IMG_GRUB -eq 1 ] && [ -f "${EFI_IMG_PATH:-/workiso/efi.img}" ] && command -v mcopy >/dev/null 2>&1; then
      mcopy -o -i "$EFI_IMG_PATH" "$GRUB_EFI" ::/EFI/BOOT/grubaa64.efi || true
      log "Updated grubaa64.efi into efi.img"
    fi
  fi
fi

# =========================
# 步骤 10（容器内，可选）：签名内核（SM2+SM3）
#   - 若为 gzip 压缩内核，临时解压/回写；
#   - 若签名失败，打印 WARN 并继续。
# =========================
if [ "${SIGN_KERNEL:-false}" = "true" ]; then
  if [ ! -r "$KERNEL_IMG" ]; then
    echo "[WARN] Kernel image not found: $KERNEL_IMG, skip kernel signing" >&2
  else
    log "Step 14: Sign kernel with SM2 ($SM2_DIGEST)"
    if file -b "$KERNEL_IMG" | grep -qi 'gzip compressed'; then
      cp -f "$KERNEL_IMG" "$KERNEL_IMG.gz"
      gzip -d -f "$KERNEL_IMG.gz"
      if pesign -n "$NSS_DIR" -c "$SM2_CERT_NICKNAME" -s -d "$SM2_DIGEST" -i "$KERNEL_IMG" -o "$KERNEL_IMG.signed" --pinfile "$PIN_FILE" --force; then
        mv -f "$KERNEL_IMG.signed" "$KERNEL_IMG"
      else
        echo "[WARN] Kernel signing failed (possibly unsupported digest/alg). Skipping kernel signing."
      fi
    else
      if ! pesign -n "$NSS_DIR" -c "$SM2_CERT_NICKNAME" -s -d "$SM2_DIGEST" -i "$KERNEL_IMG" -o "$KERNEL_IMG" --pinfile "$PIN_FILE" --force; then
        echo "[WARN] Kernel signing failed (possibly unsupported digest/alg). Skipping kernel signing."
      fi
    fi
    pesign -S -i "$KERNEL_IMG" || true
    log "Kernel signed in-place: $KERNEL_IMG"
  fi
fi

# =========================
# 步骤 11（容器内）：将签名后的 EFI 回写到 efi.img（使用 mtools，无需 root）
# =========================
if [ -f "${EFI_IMG_PATH:-/workiso/efi.img}" ] && command -v mcopy >/dev/null 2>&1; then
  if [ -f "$OUTPUT_EFI" ]; then
    mcopy -o -i "$EFI_IMG_PATH" "$OUTPUT_EFI" ::/EFI/BOOT/bootaa64.efi || true
    log "Updated bootaa64.efi into efi.img"
  fi
fi

# =========================
# 步骤 12（容器内）：基于 /workiso 重打包 ISO（xorriso）
# =========================
if [ "${REPACK_ISO:-false}" = "true" ]; then
  log "Step 16: Repack ISO using xorriso"
  if ! command -v xorriso >/dev/null 2>&1; then
    (yum -y install xorriso || dnf -y install xorriso) || echo "[WARN] xorriso install failed; skip ISO repack"
  fi
  if command -v xorriso >/dev/null 2>&1; then
    (cd /workiso && xorriso -as mkisofs -R -J -V "$ISO_VOLID" -o "$ISO_OUTPUT" -eltorito-alt-boot -e efi.img -no-emul-boot .) || echo "[WARN] xorriso repack failed"
    if [ -f "$ISO_OUTPUT" ]; then
      log "ISO repacked at: $ISO_OUTPUT"
    fi
  fi
fi

# =========================
# 步骤 13（容器内）：导出 DER 格式证书（便于 UEFI DB 导入）
# =========================
if [ -f "$RSA_CRT" ]; then
  openssl x509 -in "$RSA_CRT" -out /root/rsa.der -outform der || true
fi
if [ -f "$SM2_CRT" ]; then
  openssl x509 -in "$SM2_CRT" -out /root/sm2.der -outform der || true
fi
INCONTAINER

echo "[INFO] Done. Signed file should be in container at: $OUTPUT_EFI"

# =========================
# 步骤 14（宿主）：将 ISO 产物回拷到宿主机当前目录
# =========================
if [ "$REPACK_ISO" = "true" ]; then
  if docker exec -i "$CONTAINER" bash -lc "test -r '$ISO_OUTPUT'"; then
    docker cp "$CONTAINER":"$ISO_OUTPUT" ./secure.iso
    echo "[INFO] Copied ISO to host: ./secure.iso"
  else
    echo "[WARN] ISO_OUTPUT not found in container: $ISO_OUTPUT"
  fi
fi

# =========================
# 步骤 15（宿主）：复制其他关键产物到 artifacts 目录
#   - bootaa64.efi.signed / efi.img / rsa.der / sm2.der
# =========================
mkdir -p artifacts
for f in /root/rsa.der /root/sm2.der /root/bootaa64.efi.signed /workiso/efi.img; do
  if docker exec -i "$CONTAINER" bash -lc "test -r '$f'"; then
    base=$(basename "$f")
    docker cp "$CONTAINER":"$f" "./artifacts/$base"
    echo "[INFO] Copied artifact: artifacts/$base"
  fi
done